package mail;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.BodyPart;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;

import crypto.DigestManager;
import crypto.KeyMaker;
import crypto.SignatureManager;

public class Correo
{

	public static final int CORREOS_PAGINA = 2;
	
	private Session sesion;

	private Store store;
	
	private String smtp;
	
	private String password;
	
	private static Correo instancia;
	
	private Correo(){}

	public String darContenidoMensaje(Message mensaje) throws IOException, MessagingException
	{
		Object contenido = mensaje.getContent();
		if (contenido instanceof Multipart)
		{
			StringBuffer contMensaje = new StringBuffer();
			Multipart multipart = (Multipart)contenido;
			for (int i = 0; i < multipart.getCount(); i++) 
			{
				Part parte = multipart.getBodyPart(i);
				if(parte.isMimeType("text/plain"))
					contMensaje.append(parte.getContent().toString());
				else
					contMensaje.append("*****Contenido no soportado***** \n");
				
				//TODO mirar si se puede meter por ahi
//				else if (parte.isMimeType("image/*"))
//				{
//				   ImageIcon icono = new ImageIcon(ImageIO.read(unaParte.getInputStream()));
//				   JLabel l = new JLabel(icono);
//				}
			}
			return contMensaje.toString();
		}
		else
			return contenido.toString();
	}
	
	public MimeBodyPart adjuntar(String rutaAcrchivo) throws MessagingException
	{
		  MimeBodyPart mbp = new MimeBodyPart();
	      FileDataSource fds = new FileDataSource(rutaAcrchivo);
	      mbp.setDataHandler(new DataHandler(fds));
	      mbp.setFileName(fds.getName());
	      return mbp;
	}
	
	public void enviarCorreo(String de, String para, String asunto, String contenido) throws Exception 
	{
		Properties props = new Properties();
		props.put("mail.smtp.host", smtp);
		props.put("mail.smtp.socketFactory.port", "465");
		props.put("mail.smtp.socketFactory.class",
				"javax.net.ssl.SSLSocketFactory");
		props.put("mail.smtp.auth", "true");
		props.put("mail.smtp.port", "465");
 
		Session session = Session.getInstance(props);

//        session.setDebug(true);

		Message newMessage = new MimeMessage(session);
		newMessage.setFrom(new InternetAddress(de));
		newMessage.setRecipient(Message.RecipientType.TO, new InternetAddress(para));
		newMessage.setSubject(asunto+"(:P)normal");
		newMessage.setSentDate(new Date());
		newMessage.setText(contenido);
		KeyMaker.generateAES();
		CryptoMail.encriptarMensaje((MimeMessage) newMessage);
		Transport t = session.getTransport("smtp");
		t.connect(de, password);
		t.sendMessage(newMessage, newMessage.getAllRecipients());
		t.close();
	}
	
	public void enviarCorreoCrypto(String de, String para, String asunto, String contenido) throws Exception 
	{
		Properties props = new Properties();
		props.put("mail.smtp.host", smtp);
		props.put("mail.smtp.socketFactory.port", "465");
		props.put("mail.smtp.socketFactory.class",
		"javax.net.ssl.SSLSocketFactory");
		props.put("mail.smtp.auth", "true");
		props.put("mail.smtp.port", "465");
		
		Session session = Session.getInstance(props);
		
//        session.setDebug(true);
		
		Message newMessage = new MimeMessage(session);
		newMessage.setFrom(new InternetAddress(de));
		newMessage.setRecipient(Message.RecipientType.TO, new InternetAddress(para));
		newMessage.setSubject(asunto+"(:P)encriptado");
		newMessage.setSentDate(new Date());
		newMessage.setContent(contenido,"text/plain");
		newMessage.setContent(CryptoMail.encriptarMensaje((MimeMessage) newMessage));
		Transport t = session.getTransport("smtp");
		t.connect(de, password);
		t.sendMessage(newMessage, newMessage.getAllRecipients());
		t.close();
	}
	
	public void enviarCorreoFirmado(String de, String para, String asunto, String contenido) throws Exception 
	{
		Properties props = new Properties();
		props.put("mail.smtp.host", smtp);
		props.put("mail.smtp.socketFactory.port", "465");
		props.put("mail.smtp.socketFactory.class",
		"javax.net.ssl.SSLSocketFactory");
		props.put("mail.smtp.auth", "true");
		props.put("mail.smtp.port", "465");
		
		Session session = Session.getInstance(props);
		
		
//        session.setDebug(true);
		
		Message newMessage = new MimeMessage(session);
		newMessage.setFrom(new InternetAddress(de));
		newMessage.setRecipient(Message.RecipientType.TO, new InternetAddress(para));
		newMessage.setSubject(asunto+"(:P)firmado");
		newMessage.setSentDate(new Date());
		newMessage.setContent(contenido,"text/plain");
		
		newMessage.setContent(SignatureManager.firmar(contenido.getBytes(), SignatureManager.RUTA_PUB), "text/plain");
		
		
		Transport t = session.getTransport("smtp");
		t.connect(de, password);
		t.sendMessage(newMessage, newMessage.getAllRecipients());
		t.close();
	}
	
	
	public void enviarCorreoEncriptadoFirmado(String de, String para, String asunto, String contenido) throws Exception 
	{
		Properties props = new Properties();
		props.put("mail.smtp.host", smtp);
		props.put("mail.smtp.socketFactory.port", "465");
		props.put("mail.smtp.socketFactory.class",
		"javax.net.ssl.SSLSocketFactory");
		props.put("mail.smtp.auth", "true");
		props.put("mail.smtp.port", "465");
		
		Session session = Session.getInstance(props);
		
//        session.setDebug(true);
		
		Message newMessage = new MimeMessage(session);
		newMessage.setFrom(new InternetAddress(de));
		newMessage.setRecipient(Message.RecipientType.TO, new InternetAddress(para));
		newMessage.setSubject(asunto+"(:P)encriptado-firmado");
		newMessage.setSentDate(new Date());
		newMessage.setContent(contenido,"text/plain");
		newMessage.setContent(CryptoMail.encriparYFirmarMensaje(newMessage));
		
		Transport t = session.getTransport("smtp");
		t.connect(de, password);
		t.sendMessage(newMessage, newMessage.getAllRecipients());
		t.close();
	}
	
	
	
	public void enviarCorreoFirmadoHMAC(String de, String para, String asunto, String contenido) throws Exception 
	{
		Properties props = new Properties();
		props.put("mail.smtp.host", smtp);
		props.put("mail.smtp.socketFactory.port", "465");
		props.put("mail.smtp.socketFactory.class",
		"javax.net.ssl.SSLSocketFactory");
		props.put("mail.smtp.auth", "true");
		props.put("mail.smtp.port", "465");
		
		Session session = Session.getInstance(props);
		
//        session.setDebug(true);
		
		
		Message newMessage = new MimeMessage(session);
		newMessage.setFrom(new InternetAddress(de));
		newMessage.setRecipient(Message.RecipientType.TO, new InternetAddress(para));
		newMessage.setSubject(asunto+"(:P)firmado-HMAC");
		newMessage.setSentDate(new Date());
		newMessage.setContent(contenido,"plain/text");
		newMessage.setContent(CryptoMail.firmarMensajeHMAC(CryptoMail.getByteArray(contenido)), "text/plain");
		
		Transport t = session.getTransport("smtp");
		t.connect(de, password);
		t.sendMessage(newMessage, newMessage.getAllRecipients());
		t.close();
	}
	
	

	public void conectar(String tipo, String login, String password, String serv, String servSMTP) throws MessagingException 
	{
		smtp = servSMTP;
		this.password = password;
		
		Properties prop = new Properties();
		if(tipo.equals("pop3"))
		{
			prop.setProperty("mail.pop3.starttls.enable", "false");
			prop.setProperty("mail.pop3.socketFactory.class","javax.net.ssl.SSLSocketFactory" );
			prop.setProperty("mail.pop3.socketFactory.fallback", "false");
			prop.setProperty("mail.pop3.port","995");
			prop.setProperty("mail.pop3.socketFactory.port", "995");
		}
		else if (tipo.equals("imap"))
		{
			prop.setProperty("mail.imap.starttls.enable", "false");
			prop.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
			prop.setProperty("mail.imap.socketFactory.fallback", "false");
			prop.setProperty("mail.imap.port", "993");
			prop.setProperty("mail.imap.socketFactory.port", "993");
		}
		sesion = Session.getDefaultInstance(prop);
		
		store = sesion.getStore(tipo);
		store.connect(serv, login, password);
		
	}

	public ArrayList<Message> descargarMensajes(int pagina) throws MessagingException
	{
		Folder inbox = store.getFolder("INBOX");
		inbox.open(Folder.READ_WRITE);
		
		int numMensaje = inbox.getMessageCount();
		int count = 0;
		
		ArrayList<Message> mensajes = new ArrayList<Message>();
		
		//Solo descargue N mensajes
		while(count < CORREOS_PAGINA)
		{
			mensajes.add(inbox.getMessage(numMensaje-CORREOS_PAGINA*(pagina-1)));
			count++;
			numMensaje--;	
		}		
		return mensajes;
	}
	
	public int darTotalCorreos()
	{
		int numero = 0;
		
		try 
		{
			Folder inbox = store.getFolder("INBOX");
			inbox.open(Folder.READ_WRITE);
			numero = inbox.getMessageCount();
		} 
		catch (MessagingException e) 
		{
			e.printStackTrace();
		}
		return numero;
	}
	
	public static Correo getInstance()
	{
		if(instancia == null)
			instancia = new Correo();
		return instancia;
	}

	public String darContenidoMensajeEncriptado(Message mensajeSeleccionado) throws IOException, MessagingException, InvalidKeyException, IllegalStateException {
		
		return CryptoMail.desencriptarMensaje(mensajeSeleccionado.getContent());
	}

	public String darContenidoMensajeFirmado(Message mensajeSeleccionado) throws IOException, MessagingException {
		
		return CryptoMail.verificarMensajeFirmado(mensajeSeleccionado.getContent());
	}

	public String darContenidoMensajeFirmadoHMAC(Message mensajeSeleccionado) throws IOException, MessagingException {
		
		Multipart multipart = (Multipart) mensajeSeleccionado.getContent();
		return CryptoMail.verificarMensajeFirmadoHMAC(multipart);
	}

	public String darContenidoMensajeEncriptadoFirmado(Message mensajeSeleccionado) throws IOException, MessagingException, InvalidKeyException, IllegalStateException {
		Multipart multipart = (Multipart) mensajeSeleccionado.getContent();
		return CryptoMail.verificarMensajeEncriptadoFirmado(multipart);
	}
}
